package 泛型;

import java.util.*;

public class 泛型 {
	public static void main(String[] args){
		ArrayList<String> list = new ArrayList<String>();
		list.add("Hello");
		list.add(" second");
		// 获取到Object，必须强制转型为String:
		String first = (String) list.get(0);
		String second=(String) list.get(1);
		System.out.println(first+second);
		
		ArrayList<Object> list1=new ArrayList<>();
		list1.add(Integer.valueOf(100));
		
		List list3=new ArrayList<>();
		list3.add("third");
		String third=(String) list3.get(0);
		System.out.println(third);
		
		ArrayList<Number> list4=new ArrayList<Number>();
		list4.add(Integer.valueOf(100));
		list4.add(Float.valueOf((float) 12.34));
		Number firstNumber=list4.get(0);
		Number secondNumber=list4.get(1);
		System.out.println(firstNumber);
		System.out.println(secondNumber);
		
//		ArrayList<Number>=list4;
	}
}
/*
把ArrayList变成一种模板：ArrayList<T>，代码如下：
public class ArrayList<T> {
    private T[] array;
    private int size;
    public void add(T e) {...}
    public void remove(int index) {...}
    public T get(int index) {...}
}
T可以是任何class。这样一来，我们就实现了：编写一次模版，可以创建任意类型的ArrayList：
因此，泛型就是定义一种模板，例如ArrayList<T>，然后在代码中为用到的类创建对应的ArrayList<类型>：
ArrayList<String> strList = new ArrayList<String>();

泛型，即“参数化类型”。一提到参数，最熟悉的就是定义方法时有形参，然后调用此方法时传递实参。那么参数化类型怎么理解呢？
顾名思义，就是将类型由原来的具体的类型参数化，类似于方法中的变量参数，此时类型也定义成参数形式（可以称之为类型形参），
然后在使用/调用时传入具体的类型（类型实参）。
泛型的本质是为了参数化类型（在不创建新的类型的情况下，通过泛型指定的不同类型来控制形参具体限制的类型）。也就是说在泛型使用过程中，
操作的数据类型被指定为一个参数，这种参数类型可以用在类、接口和方法中，分别被称为泛型类、泛型接口、泛型方法。

*/
/*public class 泛型 {
	public static void main(String[] args){
//		ArrayList list = new ArrayList();
		// 创建可以存储String的ArrayList:
		ArrayList<String> strList = new ArrayList<String>();
		// 创建可以存储Float的ArrayList:
		ArrayList<Float> floatList = new ArrayList<Float>();
		// 创建可以存储Person的ArrayList:
//		ArrayList<Person> personList = new ArrayList<Person>();
		ArrayList<Integer> intList=new ArrayList<Integer>();
		strList.add("Hello");
		intList.add(123);
		// 获取到Object，必须强制转型为String:
		String first = (String) strList.get(0);
		Integer second=intList.get(0);
		System.out.println(first+second);

		List<String> stringArrayList = new ArrayList<String>();
		List<Integer> integerArrayList = new ArrayList<Integer>();

		Class classStringArrayList = stringArrayList.getClass();
		Class classIntegerArrayList = integerArrayList.getClass();

		if(classStringArrayList.equals(classIntegerArrayList)){
//		    Log.d("泛型测试","类型相同");
		}
	}
}*/

/*一、泛型方法

你可以写一个泛型方法，该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型，编译器适当地处理每一个方法调用。
下面是定义泛型方法的规则：

所有泛型方法声明都有一个类型参数声明部分（由尖括号分隔），该类型参数声明部分在方法返回类型之前（在下面例子中的<E>）。
每一个类型参数声明部分包含一个或多个类型参数，参数间用逗号隔开。一个泛型参数，也被称为一个类型变量，是用于指定一个泛型类型名称的标识符。
类型参数能被用来声明返回值类型，并且能作为泛型方法得到的实际参数类型的占位符。
泛型方法方法体的声明和其他方法一样。注意类型参数只能代表引用型类型，不能是原始类型（像 int,double,char 的等）。
实例
下面的例子演示了如何使用泛型方法打印不同字符串的元素：
*/

/*public class 泛型 {
	// 泛型方法 printArray
	public static <E> void printArray(E[] inputArray) {
		// 输出数组元素
		for (E element : inputArray) {
			System.out.printf("%s ", element);
		}
		System.out.println();
	}

	public static void main(String args[]) {
		// 创建不同类型数组： Integer, Double 和 Character
		Integer[] intArray = { 1, 2, 3, 4, 5 };
		Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
		Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

		System.out.println("Array integerArray contains:");
		printArray(intArray); // 传递一个整型数组

		System.out.println("\nArray doubleArray contains:");
		printArray(doubleArray); // 传递一个双精度型数组

		System.out.println("\nArray characterArray contains:");
		printArray(charArray); // 传递一个字符型型数组
	}
}*/


//有界的类型参数:
//
//可能有时候，你会想限制那些被允许传递到一个类型参数的类型种类范围。例如，一个操作数字的方法可能只希望接受 Number 或者 Number 子类的实例。
//这就是有界类型参数的目的。
//要声明一个有界的类型参数，首先列出类型参数的名称，后跟 extends 关键字，最后紧跟它的上界。
//
//例如，对数字进行操作的方法可能只想接受 Number 实例或其子类。这是有界的类型参数。
//
//要声明有界的类型参数，请列出类型参数的名称，后跟 extends 关键字，后跟其上限，在本例中为 Number。注意，extends 在一般意义上用来表示「扩展」
//（如在类中）或「实现」（如在接口中）。

//public class Box<T> {
//
//    private T t;          
//
//    public void set(T t) {
//        this.t = t;
//    }
//
//    public T get() {
//        return t;
//    }
//
//    public <U extends Number> void inspect(U u){
//        System.out.println("T: " + t.getClass().getName());
//        System.out.println("U: " + u.getClass().getName());
//    }
//
//    public static void main(String[] args) {
//        Box<Integer> integerBox = new Box<Integer>();
//        integerBox.set(new Integer(10));
//        integerBox.inspect("some text"); // error: this is still String!
//    }
//}
//通过修改我们的泛型方法来包含这个有界的类型参数，编译将会失败，因为我们的调用 inspect 仍然包含 String：
//
//Box.java:21: <U>inspect(U) in Box<java.lang.Integer> cannot
//  be applied to (java.lang.String)
//                        integerBox.inspect("10");
//                                  ^
//1 error
//除了限制可用于实例化泛型的类型之外，有界的类型参数还允许您调用在边界中定义的方法：

//public class NaturalNumber<T extends Integer> {
//
//    private T n;
//
//    public NaturalNumber(T n)  { this.n = n; }
//
//    public boolean isEven() {
//        return n.intValue() % 2 == 0;
//    }
//
//    // ...
//}
//isEven 方法通过类 T 调用的 Integer 中所定义的 intValue 方法
//
//#多重界限
//前面的例子说明了使用带有单个边界的类型参数，但是一个类型参数可以有多个边界：
//
//<T extends B1 & B2 & B3>
//具有多个边界的类型变量是边界中列出的所有类型的子类型。如果其中一个边界是一个类，则必须先指定它。例如：
//
//Class A { /* ... */ }
//interface B { /* ... */ }
//interface C { /* ... */ }
//
//class D <T extends A & B & C> { /* ... */ }
//如果未先指定绑定A，则会出现编译时错误：
//
//class D <T extends B & A & C> { /* ... */ }  // compile-time error
//
//下面的例子演示了 "extends" 如何使用在一般意义上的意思 "extends"（类）或者"implements"（接口）。该例子中的泛型方法返回三个可比较对象的最大值。

/*public class 泛型{
   // 比较三个值并返回最大值
   public static <T extends Comparable<T>> T maximum(T x, T y, T z){                     
      T max = x; // 假设x是初始最大值
      if ( y.compareTo( max ) > 0 ){
         max = y; //y 更大
      }
      if ( z.compareTo( max ) > 0 ){
         max = z; // 现在 z 更大           
      }
      return max; // 返回最大对象
   }
   public static void main( String args[] )
   {
      System.out.printf( "Max of %d, %d and %d is %d\n\n",
                   3, 4, 5, maximum( 3, 4, 5 ) );
 
      System.out.printf( "Maxm of %.1f,%.1f and %.1f is %.1f\n\n",
                   6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );
 
      System.out.printf( "Max of %s, %s and %s is %s\n","pear",
         "apple", "orange", maximum( "pear", "apple", "orange" ) );
   }
}*/

//泛型方法和有界类型参数
//有界类型参数是通用算法实现的关键，请考虑以下方法，该方法计算数组T[]中大于指定元素elem的元素数。
//
//public static <T> int countGreaterThan(T[] anArray, T elem) {
//    int count = 0;
//    for (T e : anArray)
//        if (e > elem)  // compiler error
//            ++count;
//    return count;
//}
//该方法的实现很简单，但它不能编译，因为大于运算符（>）仅适用于基本类型，如short、int、double、long、float、byte和char，你不能使用>运
//算符来比较对象，要解决此问题，请使用由Comparable<T>接口限定的类型参数：
//
//public interface Comparable<T> {
//    public int compareTo(T o);
//}
//生成的代码将是：
//
//public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
//    int count = 0;
//    for (T e : anArray)
//        if (e.compareTo(elem) > 0)
//            ++count;
//    return count;
//}



/*二、泛型类
泛型类的声明和非泛型类的声明类似，除了在类名后面添加了类型参数声明部分。
和泛型方法一样，泛型类的类型参数声明部分也包含一个或多个类型参数，参数间用逗号隔开。一个泛型参数，也被称为一个类型变量，
是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数，这些类被称为参数化的类或参数化的类型。

如下实例演示了我们如何定义一个泛型类:*/

/*public class 泛型<T> {
 
  private T t;
 
  public void add(T t) {
    this.t = t;
  }
 
  public T get() {
    return t;
  }
 
  public static void main(String[] args) {
     泛型<Integer> integerBox = new 泛型<Integer>();
     泛型<String> stringBox = new 泛型<String>();
   
     integerBox.add(new Integer(10));
     stringBox.add(new String("Hello World"));
 
     System.out.printf("Integer Value :%d\n\n", integerBox.get());
     System.out.printf("String Value :%s\n", stringBox.get());
  }
}*/


/*三、泛型接口
除了ArrayList<T>使用了泛型，还可以在接口中使用泛型。例如，Arrays.sort(Object[])可以对任意数组进行排序，但待排序的元素必须
实现Comparable<T>这个泛型接口：

public interface Comparable<T> {
//     * 返回负数: 当前实例比参数o小
//     * 返回0: 当前实例与参数o相等
//     * 返回正数: 当前实例比参数o大
    int compareTo(T o);
}
可以直接对String数组进行排序：
*/

// sort
//import java.util.Arrays;
/*
public class 泛型{
    public static void main(String[] args) {
        String[] ss = new String[] { "Orange", "Apple", "Pear","Adfgdg" };
        Arrays.sort(ss);
        System.out.println(Arrays.toString(ss));
    }
}
*/
/*可以进行正常排序操作，这是因为String本身已经实现了Comparable<String>接口。如果换成我们自定义的Person类型试试：

public class 泛型 {
 public static void main(String[] args) {
     Person[] ps = new Person[] {
         new Person("Bob", 61),
         new Person("Alice", 88),
         new Person("Lily", 75),
     };
     Arrays.sort(ps);
     System.out.println(Arrays.toString(ps));

 }
}

class Person {
 String name;
 int score;
 Person(String name, int score) {
     this.name = name;
     this.score = score;
 }
 public String toString() {
     return this.name + "," + this.score;
 }
}
*/
//运行程序，我们会得到ClassCastException，即无法将Person转型为Comparable。我们修改代码，让Person实现Comparable<T>接口：

//sort

/*public class 泛型 {
	public static void main(String[] args) {
		Person2[] ps = new Person2[] { new Person2("Bob", 61), new Person2("Alice", 88), new Person2("Lily", 75), };
		Arrays.sort(ps);
		System.out.println(Arrays.toString(ps));// [Alice,88, Bob,61, Lily,75]

	}
}

class Person2 implements Comparable<Person2> {
	String name;
	Integer score;// 注意类型参数只能代表引用型类型，不能是原始类型（像 int,double,char 的等）
// int score;

	Person2(String name, Integer score) {
// Person2(String name, int score) {
		this.name = name;
		this.score = score;
	}

	public int compareTo(Person2 other) {
     return this.name.compareTo(other.name);
//		return this.score.compareTo(other.score);
	}

	public String toString() {
		return this.name + "," + this.score;
	}
}*/

//运行上述代码，可以正确实现按name进行排序。也可以修改比较逻辑，例如，按score从高到低排序。

/*
 * 多个泛型类型 泛型还可以定义多种类型。例如，我们希望Pair不总是存储两个类型一样的对象，就可以使用类型<T, K>：
 * 
 * public class Pair<T, K> { private T first; private K last; public Pair(T
 * first, K last) { this.first = first; this.last = last; } public T getFirst()
 * { ... } public K getLast() { ... } } 使用的时候，需要指出两种类型：
 * 
 * Pair<String, Integer> p = new Pair<>("test", 123); Java标准库的Map<K,
 * V>就是使用两种泛型类型的例子。它对Key使用一种类型，对Value使用另一种类型。
 */


//泛型即可以理解为把数据类型作为参数，即参数化类型，用来提高代码的安全性，灵活性，避免类型转换；代码简洁明了，同时对于程序的可扩展性起到至关重要的作用。
//
//首先设计一个简单的顶层接口，只定义一个callBack方法，和对出入参数的简单逻辑设定，这种设计在Java的源码设计中随处可见，例如上面的集合体系：
//
///**
// * 基础接口设计
// * @param <R> 返参类型
// * @param <V> 入参类型
// */
/*
interface CallBack<R,V> {
    // 回调方法：V 方法入参 ，R 方法返回值
    R callBack (V v) ;
}
//为了实现具体的业务，再基于顶层接口向下做扩展，这里声明两个扩展接口，作为具体业务类的接口：
//
//扩展接口设计01
interface ExtCallBack01<R extends Integer,V extends List<String>>
                        extends CallBack<Integer,List<String>> {
    @Override
    Integer callBack (List<String> list) ;
}
//扩展接口设计01
interface ExtCallBack02<R extends Boolean,V extends Map<String,Long>>
        extends CallBack<Boolean,Map<String,Long>> {
    @Override
    Boolean callBack (Map<String,Long> map) ;
}
//这样可以通过扩展接口去设计具体的业务类，提高程序的灵活可扩展性：
//
//public class Generic02 {
public class 泛型 {
    public static void main(String[] args) {
    	System.out.println("sdasdadsas");
        new ExtCallBack01<Integer,List<String>>(){
            @Override
            public Integer callBack(List<String> list) {
                list.add("hello");
                System.out.println(list.size());
                return list.size();
            }
        };
        new ExtCallBack02<Boolean,Map<String,Long>> (){
            @Override
            public Boolean callBack(Map<String, Long> map) {
                map.put("Fire",119L) ;
                return map.size()>0 ;
            }
        } ;
    }
}
*/
//通过上面这个案例，可以清楚的感觉到泛型机制的灵活和强大。
//
//3、泛型本质
//泛型虽然可以使用在类，接口，方法，参数等各个地方，但是其约束能力是在代码的编译期：
//
//public class Generic03 {
//    public static void main(String[] args) {
//        DefEntry<String> defEntry1 = new DefEntry<>("hello") ;
//        DefEntry<Long> defEntry2 = new DefEntry<>(999L) ;
//        // Always True
//        System.out.println(defEntry1.getClass()==defEntry2.getClass());
//    }
//}
//class DefEntry<T> {
//    private T param ;
//    public DefEntry (T param){
//        this.param = param ;
//    }
//}
//编译过程中，会对泛型合法性作校验，校验成功编译的class文件没有泛型信息，即泛型擦除掉，通过一个简单的命令查看编译后的文件：
//
//javap -v Generic03.class
//01-1.jpeg
//当然这也会带来安全问题：
//
//public static void main(String[] args) throws Exception {
//    Map<String, String> map = new HashMap<>();
//    Method method = HashMap.class.getDeclaredMethod("put",
//                                new Class[] { Object.class, Object.class });
//    method.invoke(map,888L, 999L);
//    // {888=999}
//    System.out.println(map);
//    // java.lang.ClassCastException
//    System.out.println(map.get(888L));
//}
//这里即通过反射的机制，绕开泛型，在map中放入程序语义上的非法值类型，在运行过程中获取值的时候才抛出类型转换异常。


